#----------------------------------------------------------------------
#
#  Copyright 2007, Thomas Dejanovic.
# 
#  This is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation; either version 2.1 of
#  the License, or (at your option) any later version.
# 
#  This software is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#  Lesser General Public License for more details.
# 
#  You should have received a copy of the GNU Lesser General Public
#  License along with this software; if not, write to the Free
#  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
#  02110-1301 USA, or see the FSF site: http://www.fsf.org.
#
#----------------------------------------------------------------------
#
#  This is contians some utility functions for generating verilog.
#
#----------------------------------------------------------------------
id = "$Id: hatch_verilog.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_targets/verilog/obsolete/hatch_verilog.py $
# $Author: jayshurtz $
#----------------------------------------------------------------------

version = " ".join(id.split()[1:3])

import re
import math

#----------------------------------------------------------------------
# Notes:
#

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#----------------------------------------------------------------------

verilogHeader = """/*----------------------------------------------------------------------
 *
 * This file is generated hatch which is free software distributed
 * under the terms of the GNU Lesser General Public License.
 *
 * Hatch is hosted by google at http://code.google.com/p/hatch
 * 
 *----------------------------------------------------------------------    
 *
 * DO NOT EDIT THIS FILE, it is (or should be) automaticaly generated.
 * 
 *---------------------------------------------------------------------*/\n\n"""

#----------------------------------------------------------------------

def addr_string(a,w=32,m=0xffffffffL):
    """return a string formated as x'hxxx using the address, width and mask supplied."""
    return "%i'h%x"%(w,a&m)

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def value_string(s, w):
    """return a value formated as a verilog number of the width given.
    If the string given contains an expression, just return the
    expression."""

    # convert it to a string because I can;t figure out how to type
    # check the sucker.
    s = s.__str__()

    r = ""
    try:
        # is it just a number
        v = int(s, 0)
        if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
            raise ValueError, "Width of number is bigger than can be accomodated by the register."
        r = "%d'h%x"%(w, v)
    except ValueError:
        # more complicated than just a number.
        m1 = re.match("^\s*0x(?P<hex>[a-fA-f0-9_]+)\s*$", s)
        m2 = re.match("^\s*(?P<width>\d+)`h(?P<hex>[a-fA-f0-9_]+)\s*$", s)
        m3 = re.match("^\s*(?P<width>\d+)`d(?P<dec>[0-9_]+)\s*$", s)
        m4 = re.match("^\s*(?P<width>\d+)`b(?P<bin>[01_]+)\s*$", s)
        if m1:
            # we have a hex string that did not evaluate for some reason.
            n = m1.group('hex')
            n = n.replace('_', '')
            v = int("0x%s"%(n), 0)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = "%d'h%x"%(w, v)
        elif m2:
            # we have a verilog formated hex string.
            n = m2.group('hex')
            n = n.replace('_', '')
            if int(m2.group('width')) != w:
                raise ValueError, "Width of number does not match the register width."
            r = "%d'h%s"%(w, n)
        elif m3:
            # we have a verilog formated decimal string.
            n = m3.group('dec')
            n = n.replace('_', '')
            if int(m3.group('width')) != w:
                raise ValueError, "Width of number does not match the register width."
            v = int(n)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = "%d'h%x"%(w, v)
        elif m4:
            # we have a verilog formated binary string.
            n = m4.group('bin')
            n = n.replace('_', '')
            if int(m4.group('width')) != w:
                raise ValueError, "Width of number does not match the register width."
            v = int(n, 2)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = "%d'h%x"%(w, v)
        else:
            # this is an expression of some sort.
            r = s
            # XXXX - I should put in expression result width checking
            # here at some time.

    return r

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def signal_list(s):
    """return a list of valid verilog signal names in the verilog
    expression given."""

    l=[]

    s = s.translate("".join([chr(i) for i in range(256)]), "()=!~&+|<>?:,{}")    # remove invalid chars only. # TODO use (string.maketrans("",""), string.punctuation) ?

    m1 = re.match("^\s*(?P<signal>[a-zA-Z][a-zA-Z_0-9]+)\s*\[\s*(?P<rem>.*)\s*\]\s*$", s)
    m2 = re.match("^\s*(?P<signal>[a-zA-Z][a-zA-Z_0-9]+)\s*[\&\|]\s*(?P<rem>.+)\s*$", s)
    m3 = re.match("^\s*(?P<signal>[a-zA-Z][a-zA-Z_0-9]+)\s+(?P<rem>.+)\s*$", s)
    m4 = re.match("^\s*(?P<signal>[a-zA-Z][a-zA-Z_0-9]+)\s*$", s)
    m5 = re.match("^\s*\d+'[hdb][a-fA-F0-9]+\s+(?P<rem>.*)\s*$", s)

    if m1 :
        sl = m1.group('rem')
        if len(sl) > 0:
            l += signal_list(m1.group('rem'))
        l.append(m1.group('signal'))

    if m2 :
        sl = m2.group('rem')
        if len(sl) > 0:
            l += signal_list(m2.group('rem'))
        l.append(m2.group('signal'))

    if m3 :
        sl = m3.group('rem')
        if len(sl) > 0:
            l += signal_list(m3.group('rem'))
        l.append(m3.group('signal'))

    if m4 :
        l.append(m4.group('signal'))

    if m5 :
        sl = m5.group('rem')
        if len(sl) > 0:
            l += signal_list(m5.group('rem'))

    return l

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def reduce_declaration_list (rList):
    """reduce the list of wire and register definitions.

    wire and reg definitions can multiply define the same register as
    long as the width of all the definitions are the same.  the
    multiple definitions are collapsed into a single definition with
    reg taking precidence over wire.  i.e. if a function requires a
    reg and all the others require a wire, a reg will be defined."""

    rHash = {}
    for (t, w, n) in rList:
        if rHash.has_key(n) :
            if w != None and rHash[n][1] != None and w != rHash[n][1]:
                raise "*** ERROR - declaration of %s has mutiple widths [%s, %s]."%(n, w, rHash[n][1])
            if t == 'reg ':
                rHash[n][0] = t
            if w != None:
                rHash[n][1] = w
        else:
            rHash[n] = [t, w]
        
    # build the new list.
    r = []
    k = rHash.keys()
    k.sort()
    for n in k:
        r.append([rHash[n][0], rHash[n][1], n])

    return r

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#----------------------------------------------------------------------
